package com._4cmd.store.connection;


import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.log4j.Logger;


import com._4cmd.conf.Configuration;
import com._4cmd.conf.ConfigurationConstants;
import com._4cmd.conf.DefaultConfigurationImpl;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class ConnectionPoolC3P0Impl implements ConnectionPool {

	private static Logger logger = Logger.getLogger(ConnectionPoolC3P0Impl.class);

	private static ConnectionPoolC3P0Impl instance = null;

	private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();


	private ComboPooledDataSource dataSource = null;


	public static ConnectionPoolC3P0Impl getInstance()
			throws Exception {
		if (null == instance) {
			synchronized (ConnectionPoolC3P0Impl.class) {
				if (null == instance) {
					try {
						instance = new ConnectionPoolC3P0Impl();
					} catch (Exception e) {
						String msg = "Init Connection Pool error.";
						logger.error(msg,e);
						throw e;
					}
				}
			}
		}
		return instance;
	}

	private ConnectionPoolC3P0Impl() throws Exception {
		createDataSource(getDBConf());
		try {
			getConnection().close();
		} catch (SQLException e) {
			String msg = "Init connection pool error .";
			throw new Exception(msg);
		}
	}

	private ComboPooledDataSource createDataSource(Properties property) throws Exception {
		if (this.dataSource == null) {
			this.dataSource = new ComboPooledDataSource();
			String driverClass = property.getProperty("driverClass");
			if (driverClass != null) {
				try {
					dataSource.setDriverClass(driverClass);
				} catch (PropertyVetoException e) {
					String msg = "can not set the driver class.";
					throw new Exception(msg,e);
				}
			}
			String jdbcUrl = property.getProperty("jdbcUrl");
			if (jdbcUrl != null) {
				dataSource.setJdbcUrl(jdbcUrl);
			}
			String user = property.getProperty("user");
			if (user != null) {
				dataSource.setUser(user);
			}
			String password = property.getProperty("password");
			if (password != null) {
				dataSource.setPassword(password);
			}
			String initialPoolSize = property.getProperty("initialPoolSize");
			if (initialPoolSize != null) {
				dataSource
						.setInitialPoolSize(Integer.parseInt(initialPoolSize));
			}
			String maxPoolSize = property.getProperty("maxPoolSize");
			if (maxPoolSize != null) {
				dataSource.setMaxPoolSize(Integer.parseInt(maxPoolSize));
			}
			String minPoolSize = property.getProperty("minPoolSize");
			if (minPoolSize != null) {
				dataSource.setMinPoolSize(Integer.parseInt(minPoolSize));
			}
			String acquireIncrement = property.getProperty("acquireIncrement");
			if (acquireIncrement != null) {
				dataSource.setAcquireIncrement(Integer
						.parseInt(acquireIncrement));
			}
			String maxStatements = property.getProperty("maxStatements");
			if (maxStatements != null) {
				dataSource.setMaxStatements(Integer.parseInt(maxStatements));
			}
			String maxStatementsPerConnection = property
					.getProperty("maxStatementsPerConnection");
			if (maxStatementsPerConnection != null) {
				dataSource.setMaxStatementsPerConnection(Integer
						.parseInt(maxStatementsPerConnection));
			}
			String numHelperThreads = property.getProperty("numHelperThreads");
			if (numHelperThreads != null) {
				dataSource.setNumHelperThreads(Integer
						.parseInt(numHelperThreads));
			}
			String debugUnreturnedConnectionStackTraces = property
					.getProperty("debugUnreturnedConnectionStackTraces");
			if (debugUnreturnedConnectionStackTraces != null) {
				dataSource.setDebugUnreturnedConnectionStackTraces(Boolean
						.parseBoolean(debugUnreturnedConnectionStackTraces));
			}
			String testConnectionOnCheckout = property
					.getProperty("testConnectionOnCheckout");
			if (testConnectionOnCheckout != null) {
				dataSource.setTestConnectionOnCheckout(Boolean
						.parseBoolean(testConnectionOnCheckout));
			}
			String idleConnectionTestPeriod = property
					.getProperty("idleConnectionTestPeriod");
			if (idleConnectionTestPeriod != null) {
				dataSource.setIdleConnectionTestPeriod(Integer
						.parseInt(idleConnectionTestPeriod));
			}
			String preferredTestQuery = property
					.getProperty("preferredTestQuery");
			if (preferredTestQuery != null) {
				dataSource.setPreferredTestQuery(preferredTestQuery);
			}
			String maxIdleTime = property.getProperty("maxIdleTime");
			if (maxIdleTime != null) {
				dataSource.setMaxIdleTime(Integer.parseInt(maxIdleTime));
			}
			String unreturnedConnectionTimeout = property
					.getProperty("unreturnedConnectionTimeout");
			if (unreturnedConnectionTimeout != null) {
				dataSource.setUnreturnedConnectionTimeout(Integer
						.parseInt(unreturnedConnectionTimeout));
			}
			String checkoutTimeout = property.getProperty("checkoutTimeout");
			if (checkoutTimeout != null) {
				dataSource
						.setCheckoutTimeout(Integer.parseInt(checkoutTimeout));
			}
		}
		return this.dataSource;
	}

	public void close(Connection conn) {
		if (conn == null)
			return;

		try {
			conn.close();
		} catch (SQLException e) {
			String strMsg = "failed to close Connection";
		}
	}

	public void close(Connection conn, PreparedStatement stmt, ResultSet rs) {
		close(stmt, rs);
		close(conn);

	}

	public void close(Connection conn, PreparedStatement stmt) {
		close(stmt);
		close(conn);

	}

	public void close(PreparedStatement stmt, ResultSet rs) {
		close(stmt);
		close(rs);

	}

	public void close(ResultSet rs) {
		if (rs == null)
			return;
		try {
			rs.close();
		} catch (SQLException e) {
			String strMsg = "failed to close ResultSet";
		}
	}

	public void close(PreparedStatement stmt) {
		if (stmt == null)
			return;

		try {
			stmt.close();
		} catch (SQLException e) {
			String strMsg = "Failed to close PreparedStatement";
		}

	}

	public void closeThreadConnection() {
		Connection conn = connThreadLocal.get();// parasoft-suppress JDBC.CDBC
												// "DB connection has been closed by close(conn)"
		if (conn != null) {
			close(conn);
		}
		connThreadLocal.remove();
	}

	public Connection getConnection() throws Exception {
		Connection conn = null;
		try {
			conn = dataSource.getConnection();
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			String msg = "Get Connection failed.";
			throw new Exception(msg, e);
		}
		return conn;
	}

	public Connection getConnection(boolean isAutoCommit) throws Exception {
		Connection conn = null;
		try {
			conn = dataSource.getConnection();
			conn.setAutoCommit(isAutoCommit);
		} catch (SQLException e) {
			String msg = "Get Connection failed.";
			throw new Exception(msg, e);
		}

		return conn;
	}

	public Connection getThreadConnection() throws Exception {
		Connection conn = null;
		try {
			if (connThreadLocal.get() == null) {
				conn = this.getConnection();
				connThreadLocal.set(conn);
			} else {
				conn = connThreadLocal.get();
				if (conn.isClosed()) {
					String msg = "The conn can not valid ,we will get the new one ,the Thread id is :"
							+ Thread.currentThread().getId();
					connThreadLocal.remove();
					conn = this.getConnection();
					connThreadLocal.set(conn);
				}
			}
		} catch (SQLException e) {
			String msg = "Get Thread Connection failed.";
			throw new Exception(msg, e);
		}
		return conn;
	}

	public void rollBack(Connection conn) {
		if(null != conn){
			try {
				conn.rollback();
			} catch (SQLException e) {
				String strMsg = "Failed to rollBack Connection";
			}
		}
	}
	
	private Properties getDBConf(){
		Configuration conf = DefaultConfigurationImpl.getInstance();
		return conf.getProps(ConfigurationConstants.DB);
	}
}
	

